package cache

import (
	"log"
	"sync"
)

// Cache 缓存接口
type Cache interface {
	Set(key string, value interface{}) //设置/添加一个缓存，如果 key 存在，则用新值覆盖旧值
	Get(key string) interface{}        //通过 key 获取一个缓存值
	Del(key string)                    //通过 key 删除一个缓存值
	DelOldest()                        //删除最“无用”的一个缓存值
	Len() int                          //获取缓存已存在的记录数
}

// DefaultMaxBytes 默认允许占用的最大内存
const DefaultMaxBytes = 1 << 29

// safeCache 并发安全缓存
type safeCache struct {
	m     sync.RWMutex
	cache Cache

	nhit, nget int //nget, nhit 记录 缓存获取次数 和 命中次数
}

// 构造函数接收一个实现了 Cache 接口的淘汰算法实现
func newSafeCache(cache Cache) *safeCache {
	return &safeCache{
		cache: cache,
	}
}

func (sc *safeCache) set(key string, value interface{}) {
	sc.m.Lock()
	defer sc.m.Unlock()
	sc.cache.Set(key, value)
}

func (sc *safeCache) get(key string) interface{} {
	sc.m.RLock()
	defer sc.m.RUnlock()
	sc.nget++
	//没有创建一个默认的 Cache 实现是避免循环引用，
	//因为前面实现的淘汰算法构造函数都返回了 Cache 接口类型，引用了 github.com/go-programming-tour-book/cache 包；
	//这个问题可以不解决，因为 safeCache 是未导出的，在使用它的地方，我们可以确保其中的 cache 字段一定非 nil。
	if sc.cache == nil {
		return nil
	}

	v := sc.cache.Get(key)
	if v != nil {
		log.Println("[TourCache] hit")
		sc.nhit++
	}
	return v
}

//定义 Stat 类型和 stat 方法，方便查看统计数据

type Stat struct {
	NHit, NGet int
}

func (sc *safeCache) stat() *Stat {
	sc.m.RLock()
	defer sc.m.RUnlock()
	return &Stat{
		NHit: sc.nhit,
		NGet: sc.nget,
	}
}
